{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 1.导入Pytorch库",
   "id": "18aa768cc4e2fe83"
  },
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "is_executing": true
    }
   },
   "source": [
    "# 导入PyTorch库，用于构建和训练神经网络\n",
    "import torch\n",
    "# 导入神经网络模块，用于定义神经网络结构\n",
    "import torch.nn as nn\n",
    "# 导入优化器模块，用于定义模型的优化算法\n",
    "import torch.optim as optim\n",
    "# 导入函数al模块，用于使用各种激活函数和损失函数\n",
    "import torch.nn.functional as F\n",
    "# 导入数据加载和数据集模块，用于处理和加载训练数据\n",
    "from torch.utils.data import DataLoader, Dataset\n",
    "# 导入变换模块，用于对图像数据进行预处理\n",
    "from torchvision import transforms"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 2.检查 GPU 支持：",
   "id": "20c3ed90472f6b53"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 根据环境选择合适的计算设备\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
   ],
   "id": "4b2c5c056a68ddee"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 3.创建自定义数据集：",
   "id": "870788dd27960a93"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "class CustomDataset(Dataset):\n",
    "    \"\"\"\n",
    "    自定义数据集类，继承自torch.utils.data.Dataset。\n",
    "    用于封装任意数据和其对应的标签，方便在神经网络模型中使用。\n",
    "\n",
    "    参数:\n",
    "    - data: 数据集，可以是任意形式，如numpy数组或Pandas DataFrame。\n",
    "    - labels: 数据集的标签，通常是一个一维数组或列表。\n",
    "    - transform: 可选的数据转换函数，用于在获取数据项时对其进行转换。\n",
    "    \"\"\"\n",
    "    def __init__(self, data, labels, transform=None):\n",
    "        \"\"\"\n",
    "        初始化自定义数据集类。\n",
    "        \"\"\"\n",
    "        self.data = data\n",
    "        self.labels = labels\n",
    "        self.transform = transform\n",
    "\n",
    "    def __len__(self):\n",
    "        \"\"\"\n",
    "        返回数据集的大小。\n",
    "\n",
    "        返回:\n",
    "        - 数据集的样本数量。\n",
    "        \"\"\"\n",
    "        return len(self.data)\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        \"\"\"\n",
    "        根据索引获取数据项。\n",
    "\n",
    "        参数:\n",
    "        - index: 索引值，用于获取数据集中的特定样本。\n",
    "\n",
    "         返回:\n",
    "         - x: 数据项。\n",
    "         - y: 数据项的标签。\n",
    "         \"\"\"\n",
    "        x = self.data[index]\n",
    "        y = self.labels[index]\n",
    "\n",
    "        # 如果提供了数据转换函数，则应用该函数对数据项进行转换\n",
    "        if self.transform:\n",
    "            x = self.transform(x)\n",
    "\n",
    "        return x, y"
   ],
   "id": "e6a8afe9619fadea"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 4.使用数据增强：",
   "id": "efe06558df18fc4e"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 定义一个转换器，用于将图像转换为一系列预处理步骤的组合\n",
    "transform = transforms.Compose([\n",
    "    # 随机水平翻转图像，增加数据集的多样性\n",
    "    transforms.RandomHorizontalFlip(),\n",
    "    # 随机旋转图像，旋转角度在(-10, 10)之间，以增强模型的泛化能力\n",
    "    transforms.RandomRotation(10),\n",
    "    # 将图像转换为Tensor，以便于在PyTorch模型中使用\n",
    "    transforms.ToTensor(),\n",
    "])"
   ],
   "id": "c8a279ce601c97af"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 5.数据加载：",
   "id": "b9116491654e8b0c"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 创建训练数据集实例\n",
    "# 参数:\n",
    "# - train_data: 训练数据输入\n",
    "# - train_labels: 训练数据标签\n",
    "# - transform: 数据转换函数\n",
    "train_dataset = CustomDataset(train_data, train_labels, transform=transform)\n",
    "test_dataset = CustomDataset(test_data, test_labels, transform=transform)\n",
    "\n",
    "# 创建数据加载器\n",
    "# 参数:\n",
    "# - train_dataset: 训练数据集实例\n",
    "# - batch_size: 每个批次的数据大小\n",
    "# - shuffle: 是否在每个epoch开始时打乱数据顺序\n",
    "train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)\n",
    "test_loader = DataLoader(test_dataset, batch_size=64, shuffle=True)"
   ],
   "id": "a71d28858cde59a0"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 6.定义神经网络模型：",
   "id": "66156e6b561a3be4"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "class Net(nn.Module):\n",
    "    \"\"\"\n",
    "    定义一个神经网络模型。继承自torch.nn.Module。\n",
    "    该模型包括一个卷积层和两个全连接层。\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        初始化模型并定义各层。\n",
    "        \"\"\"\n",
    "        super(Net, self).__init__()\n",
    "        self.conv1 = nn.Conv2d(1, 32, kernel_size=3) # 卷积层：输入通道数1，输出通道数32，卷积核大小3x3\n",
    "        self.fc1 = nn.Linear(32 * 28 * 28, 128) # 全连接层：输入大小32*28*28，输出大小128\n",
    "        self.fc2 = nn.Linear(128, 10) # 全连接层：输入大小128，输出大小10\n",
    "\n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        定义模型的前向传播过程。\n",
    "        :param x: 输入数据，一个张量\n",
    "        :return: 模型输出，一个张量\n",
    "        \"\"\"\n",
    "        x = F.relu(self.conv1(x))  # 经过卷积层后使用ReLU激活函数\n",
    "        x = x.view(x.size(0), -1)  # 将张量展平\n",
    "        x = F.relu(self.fc1(x))  # 经过第一个全连接层后使用ReLU激活函数\n",
    "        x = self.fc2(x)  # 第二个全连接层的输出，此处不使用激活函数\n",
    "        return x"
   ],
   "id": "8f911b11eaa459f2"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 7.实例化模型和优化器：",
   "id": "5a717d4de199e39b"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "#实例化神经网络模型并转移到指定设备上\n",
    "model = Net().to(device)\n",
    "\n",
    "#初始化优化器，使用Adam算法优化模型参数\n",
    "#学习率设置为0.001，以适应大多数深度学习模型的训练需求\n",
    "optimizer = optim.Adam(model.parameters(), lr=0.001)"
   ],
   "id": "91879c424c88fddf"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 8.定义损失函数：",
   "id": "89a9059b9cfa3537"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 定义损失函数为交叉熵损失，用于分类任务中\n",
    "criterion = nn.CrossEntropyLoss()"
   ],
   "id": "a3a1ea0fd887d772"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 9.训练模型：",
   "id": "3464a63b42f243fa"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "num_epochs = 1000\n",
    "log_interval = 100\n",
    "\n",
    "# 迭代训练模型，共进行num_epochs轮\n",
    "for epoch in range(num_epochs):\n",
    "    # 遍历训练数据加载器，获取批次索引、数据和目标标签\n",
    "    for batch_idx, (data, target) in enumerate(train_loader):\n",
    "        # 将数据和目标标签移动到指定设备（如GPU）\n",
    "        data, target = data.to(device), target.to(device)\n",
    "\n",
    "        # 清空梯度，避免累积\n",
    "        optimizer.zero_grad()\n",
    "        # 前向传播，获取模型输出\n",
    "        output = model(data)\n",
    "        # 计算模型输出与目标标签之间的损失\n",
    "        loss = criterion(output, target)\n",
    "        # 反向传播，计算梯度\n",
    "        loss.backward()\n",
    "        # 更新模型权重\n",
    "        optimizer.step()\n",
    "\n",
    "        # 按照日志间隔打印训练信息\n",
    "        if batch_idx % log_interval == 0:\n",
    "            print(f\"Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ({100. * batch_idx / len(train_loader):.0f}%)]\\tLoss: {loss.item():.6f}\")"
   ],
   "id": "fbcc5978f4ffd481"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 10.评估模型：",
   "id": "faf7ac76e87516f1"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "model.eval()  # 将模型设置为评估模式，以禁用dropout等仅在训练时需要的特性\n",
    "test_loss = 0  # 初始化测试损失为0\n",
    "correct = 0  # 初始化正确预测的数量为0\n",
    "with torch.no_grad():  # 在不需要计算梯度的上下文中进行评估，以减少内存消耗\n",
    "    for data, target in test_loader:  # 遍历测试数据集\n",
    "        data, target = data.to(device), target.to(device)  # 将数据和目标移动到指定设备\n",
    "        output = model(data)  # 前向传播，获取模型的输出\n",
    "        test_loss += criterion(output, target).item()  # 计算并累加测试损失\n",
    "pred = output.argmax(dim=1, keepdim=True)  # 获取预测的类别索引\n",
    "correct += pred.eq(target.view_as(pred)).sum().item()  # 计算并累加正确预测的数量\n",
    "\n",
    "test_loss /= len(test_loader.dataset)  # 计算平均测试损失\n",
    "test_accuracy = 100. * correct / len(test_loader.dataset)  # 计算测试准确率\n",
    "\n",
    "# 打印测试集的平均损失和准确率\n",
    "print(f'Test set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({test_accuracy:.0f}%)')"
   ],
   "id": "2018926539f01fc5"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 11.保存和加载模型：",
   "id": "f2fe6b56c7c967b9"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 保存模型的参数状态\n",
    "torch.save(model.state_dict(), 'model.pth')\n",
    "\n",
    "# 实例化一个新的模型对象\n",
    "model = Net()\n",
    "# 加载保存的模型参数状态到新的模型对象中\n",
    "model.load_state_dict(torch.load('model.pth'))"
   ],
   "id": "da4a8b48c52f5963"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 13.使用 TensorBoard 进行可视化：",
   "id": "4c3f05ff199be15d"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "# 创建一个SummaryWriter对象，用于将训练过程中的损失和准确率等数据写入TensorBoard日志文件\n",
    "writer = SummaryWriter()\n",
    "# 遍历训练的每个周期(epoch)\n",
    "for epoch in range(num_epochs):\n",
    "    # 进行训练和记录...\n",
    "    # 记录当前周期的训练损失\n",
    "    writer.add_scalar('Loss/train', loss.item(), epoch)\n",
    "    # 记录当前周期的训练准确率\n",
    "    writer.add_scalar('Accuracy/train', test_accuracy, epoch)\n",
    "    # ...\n",
    "\n",
    "# 关闭SummaryWriter对象，确保所有数据被正确写入日志文件\n",
    "writer.close()"
   ],
   "id": "9e975de60f8e3a0a"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 14.调整学习率：",
   "id": "5943b30f2acf3481"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 初始化学习率调度器，以便在训练过程中按策略调整学习率、\n",
    "scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)\n",
    "# 开始训练过程，迭代指定的轮次\n",
    "for epoch in range(num_epochs):\n",
    "    # 更新学习率调度器，根据当前轮次调整学习率\n",
    "    scheduler.step()  # 更新学习率\n",
    "    # 进行训练..."
   ],
   "id": "688d345425794c3d"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 15.使用预训练模型：",
   "id": "70f88a661e02be8a"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 导入torchvision中的models模块，用于访问预定义的模型架构和预训练模型\n",
    "import torchvision.models as models\n",
    "\n",
    "# 初始化一个预训练的ResNet-18模型\n",
    "# ResNet-18是一个深度卷积神经网络，具有18层，广泛应用于图像分类任务中\n",
    "# 参数pretrained=True表示使用ImageNet数据集预训练的模型权重\n",
    "model = models.resnet18(pretrained=True)"
   ],
   "id": "dceef6b11d3b9046"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# 16.迁移学习：",
   "id": "ff81c86c6ba04ab"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": [
    "# 遍历模型的所有参数\n",
    "for param in model.parameters():\n",
    "    param.requires_grad = False  # 冻结模型参数，防止在训练过程中更新这些参数\n",
    "\n",
    "# 替换最后的全连接层以适应新的类别数\n",
    "model.fc = nn.Linear(model.fc.in_features, num_classes)"
   ],
   "id": "a30513224265bbcf"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
